package com.pagoda.nerp.trade.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.pagoda.nerp.trade.admin.vendor.dto.MdAccountDto;
import com.pagoda.nerp.trade.common.ConstantDefine;
import com.pagoda.nerp.trade.common.PageListResult;
import com.pagoda.nerp.trade.common.enums.ErrorCodeMappingEnums;
import com.pagoda.nerp.trade.common.exception.BaseException;
import com.pagoda.nerp.trade.common.exception.DataRepeatException;
import com.pagoda.nerp.trade.common.extend.HttpHelper;
import com.pagoda.nerp.trade.common.httputil.HttpClientHelper;
import com.pagoda.nerp.trade.common.objectutil.CopyObject;
import com.pagoda.nerp.trade.common.objectutil.PropertyConfigurer;
import com.pagoda.nerp.trade.dao.*;
import com.pagoda.nerp.trade.entity.MdAccount;
import com.pagoda.nerp.trade.entity.MdAccountRole;
import com.pagoda.nerp.trade.entity.MdRole;
import com.pagoda.nerp.trade.entity.MdVendorAccount;
import com.pagoda.nerp.trade.service.MdAccountService;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class MdAccountServiceImpl implements MdAccountService {

    private final Logger logger = org.slf4j.LoggerFactory.getLogger(MdAccountServiceImpl.class);

    @Autowired
    private MdAccountDao mdAccountDao;

    @Autowired
    private MdAccountRoleDao mdAccountRoleDao;

    @Autowired
    private MdRoleDao mdRoleDao;

    @Autowired
    private MdVendorAccountDao mdVendorAccountDao;

    @Autowired
    private MdVendorDao mdVendorDao;

    @Autowired
    public SysBillRuleDao sysBillRuleDao;

    @Override
    public MdAccount queryAccountByOpenId(String openId) {
        return mdAccountDao.selectByOpenId(openId);
    }

    @Override
    public int getCountByAccount(String account) {
        return mdAccountDao.getCountByAccount(account);
    }

    @Override
    public int insert(MdAccount mdAccount) {
        return mdAccountDao.insert(mdAccount);
    }

    /**
     * 获取供应商子账户列表
     * 1、根据条件查询子账户列表
     * 2、获取当前列表下对应的角色信息
     * 3、拼装组合数据进行返回
     *
     * @param con
     * @return
     */
    @Override
    public Object listMdAccount(Map con) throws Exception {

        int pageNo = con.get("pageNo") != null ? Integer.parseInt(con.get("pageNo").toString()) : 1;
        int pageSize = con.get("pageSize") != null ? Integer.parseInt(con.get("pageSize").toString()) : 0;

        // 1、获取子账户信息
        Page pageInfo = PageHelper.startPage(pageNo, pageSize);
        List<MdAccount> listAccount = (List<MdAccount>) mdAccountDao.selectAllByConditon(con);

        List<MdAccountDto> listAccountDTO = new ArrayList<MdAccountDto>();

        // 2、获取各子账户的角色信息 - 单次循环获取
        //TODO 此处需优化为单次获取，而后程序进行拼接
        for (MdAccount mdAccount : listAccount) {
            MdAccountDto mdAccountDto = new MdAccountDto();
            CopyObject.fatherToChild(mdAccount, mdAccountDto);
            Map tmp = new HashMap();
            tmp.put("accountId", mdAccount.getAccountId());
            try {
                List<MdAccountRole> listMdAccountRole = mdAccountRoleDao.selectAllByConditon(tmp);
                // 单个账号对应的角色信息列表
                //TODO 此处存在问题需进一步进行优化，考虑使用mybatis的嵌套查询
                mdAccountDto.setRoleList(listMdAccountRole);
            } catch (Exception e) {
                logger.error("获取子账户角色信息失败!", e);
            }
            listAccountDTO.add(mdAccountDto);
        }
        return new PageListResult<MdAccountDto>(pageInfo, listAccountDTO);
    }

    /**
     * 新增供应商子账户数据
     * 逻辑：
     * 1、新增账户基本信息；
     * 2、绑定新账户与角色关系;
     * 3、绑定供应商与子账户信息；
     * 注意：账户本身的主从属性暂时可不关注，在关系中体现出主从账户
     *
     * @param map
     * @return 返回新增的账户ID
     */
    @Override
    @Transactional
    public int addNewMdAccount(Map map) throws Exception {
        // 参数桥接 - MAP - ENTITY
        // 1、新增账户基本属性
        MdAccount mdAccount = this.buildMdAccount(map);
        int influenceRow = this.mdAccountDao.insert(mdAccount);
        if (influenceRow <= 0) {
            throw new DataRepeatException(ErrorCodeMappingEnums.ERROR_REPEAT_PHONENUMBER);
        }

        // 2、绑定子账户与角色关系
        String roleIds = map.get("roleIds") != null ? map.get("roleIds").toString() : null;
        if (!StringUtils.isNotEmpty(roleIds)) {
            throw new RuntimeException("新增子账户中角色信息为空，执行返回!");
        }
        List<MdAccountRole> mdAccountRoleList = this.buildMdAccountRole(map, mdAccount.getAccountId());
        int accountRoleUpp = mdAccountRoleDao.batchInsert(mdAccountRoleList);
        if(accountRoleUpp <= 0){
            throw new RuntimeException("Service 更新子账户与角色关系失败，受影响行数为0");
        }
        // 3、绑定供应商与子账户关系
        MdVendorAccount mdVendorAccount = new MdVendorAccount();
        mdVendorAccount.setAccountId(mdAccount.getAccountId());
        mdVendorAccount.setVendorId(Integer.parseInt(map.get("vendorId").toString()));
        mdVendorAccount.setType(mdAccount.getAccountType());
        try {
            int mdVendorAccountUpp = mdVendorAccountDao.insert(mdVendorAccount);
            if(mdVendorAccountUpp == 0 ){
                throw new RuntimeException("Service 更新供应商与子账户关系失败，受影响行数为0");
            }
        }catch(Exception e){
            logger.error("Service 新增绑定供应商与子账户关系异常!",e);
            throw e;
        }
        return mdAccount.getAccountId();
    }

    /**
     * 删除子账户
     * 1、删除当前子账户对应的角色关系
     * 2、删除当前账号的基本数据
     *
     * @param map
     * @throws Exception
     */
    @Transactional
    @Override
    public void deleteMdAccount(Map map) throws Exception {
        // 1、删除子账户与角色关系
        int mdAccountRoleRes = mdAccountRoleDao.deleteByCondition(map);
        if (mdAccountRoleRes <= 0) {
            throw new RuntimeException("Service 执行删除账户与角色关系失败!");
        }
        // 2、删除账户数据
        int mdAccountRes = mdAccountDao.deleteByCondition(map);
        if (mdAccountRes <= 0) {
            throw new RuntimeException("Service 执行删除账失败!");
        }
        // 3、删除子账户与供应商关系
        int mdVendorAccountRes = mdVendorAccountDao.deleteByCondition(map);
        if (mdVendorAccountRes <= 0) {
            throw new RuntimeException("Service 执行解除供应商子账户关系失败!");
        }
    }

    /**
     * 设置供应商子账户权限
     * 要求：
     * 1、业务员或者财务设置为负责人时候，原负责人自动变更为业务员（）
     * 2、负责人权限切换的时候，将新负责人的手机号码更新为供应商的联系电话
     * <p>
     * 逻辑：（现阶段角色信息固定不可更新），R00001->负责人
     * 1、如果目标角色为业务员/财务，则直接绑定当前子账户的角色关系（先全清，后绑定）
     * 2、如果目标角色为负责人，且用户当前角色为非负责人，则：
     * 2.1、全清后再绑定
     * 2.2、更新当前子账户为主账户（两处），更新供应商中手机号码为当前子账户的手机号
     *
     * @param map 参数条件
     * @throws Exception
     */
    @Transactional
    @Override
    public void bindMdAccountRole(Map map) throws Exception {

        // 1、查询当前子账户信息
        MdAccount mdAccount = mdAccountDao.selectOneByCondition(map);
        if (mdAccount == null) {
            throw new BaseException(ErrorCodeMappingEnums.ERROR_INVALID_ACCOUNT);
        }

        String roleIds = map.get("roleIds").toString();
        String[] roleArr = roleIds.split(",");

        // 2、删除子账户与角色关系
        map.put("roleIds", roleArr);
        int delMdAccountRoe = mdAccountRoleDao.deleteByCondition(map);
        if (delMdAccountRoe <= 0) {
            throw new RuntimeException("Service 批量删除子账户与角色关系失败");
        }

        // 3、绑定子账户与角色关系
        List<MdAccountRole> mdAccountRoleList = this.buildMdAccountRole(map, mdAccount.getAccountId());
        int delMdAccountRoleRel = mdAccountRoleDao.batchInsert(mdAccountRoleList);
        if (delMdAccountRoleRel <= 0) {
            throw new RuntimeException("Service 批量绑定子账户与角色关系失败");
        }

        // 4、查询当前待绑定的角色信息
        List<MdRole> mdRoleList = mdRoleDao.selectAllByConditon(map);

        // 5、如果当前子账户为非主账户，且被更新为了主账户
        if (mdAccount.getAccountType().toUpperCase().equals("M")) {
            logger.info("Service 当前账户为主账户，不需要进行账户转换");
            return;
        }

        // 6、如果当前账户为非主账户，且：新增的角色中存在设置为负责人的，则进行下一步处理
        if (!this.isContainMainRole(mdRoleList)) {
            logger.info("Service 当前角色设置中未包含负责人设置，不需要更新主账户信息!");
            return;
        }

        // 逻辑：更改当前账户类型为主账户、解除原来子账户的主账户类型、更新供应商表中的手机号为该账户信息、
        // 更新供应商与子账户关系表（先全部更新为子账户，然后再为该账户进行设置）

        map.remove("accountId");

        // 7、抹去当前供应商下所有子账户类型为： A
        map.put("accountType", "A");
        int uppMdAccountDaoRes = mdAccountDao.updateStatusByCondition(map);
        if (uppMdAccountDaoRes <= 0) {
            throw new RuntimeException("Service 更新供应商账户类型为子账户失败");
        }

        // 8、重置供应商与子账户关系表子账户类型为：A
        map.put("type", "A");
        int uppMdVendorAccountDao = mdVendorAccountDao.updateByCondition(map);
        if (uppMdVendorAccountDao <= 0) {
            throw new RuntimeException("Service 更新供应商与子账户关系类型为子账户失败");
        }

        map.put("accountId", mdAccount.getAccountId());

        // 9、设置子账户类型为M
        map.put("accountType", "M");
        int uppMdAccountDaoResM = mdAccountDao.updateStatusByCondition(map);
        if (uppMdAccountDaoResM <= 0) {
            throw new RuntimeException("Service 更新供应商账户类型为主账户失败");
        }

        // 10、设置供应商与子账户关系账户类型为：M
        map.put("type", "M");
        int uppMdVendorAccountDaoM = mdVendorAccountDao.updateByCondition(map);
        if (uppMdVendorAccountDaoM <= 0) {
            throw new RuntimeException("Service 更新供应商与子账户关系类型为主账户失败");
        }

        // 11、更新供应商中手机号为当前请求手机号
        map.put("phoneNumber", mdAccount.getAccount());
        int uppMdVendorDao = mdVendorDao.updateByCondition(map);
        if (uppMdVendorDao <= 0) {
            throw new RuntimeException("Service 更新供应商手机号失败");
        }
    }


    /**
     * 判断角色中是否已包含必须唯一的角色
     * @param map
     * @return true 包含唯一角色，不可再次设置  false 不包含
     */
    public boolean isContainUniqueRole(Map map){

        // 1、如果当前供应商下已经存在了财务、负责人，则设置失败 - 根据当前待设置的角色进行查询
        String roleIds = map.get("roleIds").toString();
        String[] roleArr = roleIds.split(",");
        Map queryMdAccountRole = new HashMap();
        queryMdAccountRole.put("roleIds",roleArr);
        queryMdAccountRole.put("vendorId", map.get("vendorId").toString());
        List<MdAccountRole> mdAccountRoleListRes = mdAccountRoleDao.selectAllByConditon(queryMdAccountRole);

        // 判断当前设置的角色中是否包含：负责人、财务（code查找），仅能设置一个
        if (null == mdAccountRoleListRes || mdAccountRoleListRes.size() == 0) {
            return false;
        }
        String mainRoleCode = parseConfRoleInfo().get("mainRoleCode").toString();  // 负责人角色代码
        String financeRoleCode = parseConfRoleInfo().get("financeRoleCode").toString(); // 财务角色代码
        for(MdAccountRole accountRole : mdAccountRoleListRes){
            if(StringUtils.isNotEmpty(accountRole.getRoleCode())
                    && (accountRole.getRoleCode().equalsIgnoreCase(mainRoleCode)
                    || accountRole.getRoleCode().equalsIgnoreCase(financeRoleCode))){
                return true;
            }
        }

        // 继续设置，如果是设置当前用户角色为财务，则进行调用金融平台进行接口注册
        queryMdAccountRole.put("account", map.get("account").toString());
        queryMdAccountRole.put("vendorCode", map.get("vendorCode").toString());
        String switchStatus = PropertyConfigurer.parseStr("bank.switch.debug");
        boolean isDebug = Boolean.parseBoolean(switchStatus);
        if(isDebug){
            logger.debug("MdAccountServiceImpl -> isContainUniqueRole() 当前链接供应链金融开关未调试模式，不进行调用");
            return false;
        }
        return registryToBanking(queryMdAccountRole);
    }

    // 解析角色中的配置文件
    public Map parseConfRoleInfo(){
        String mainRoleCode = PropertyConfigurer.parseStr("mdrole.main.roleCode");  // 负责人角色代码
        String financeRoleCode = PropertyConfigurer.parseStr("mdrole.finance.roleCode"); // 财务角色代码
        Map returnMap = new HashMap();
        returnMap.put("mainRoleCode", mainRoleCode);
        returnMap.put("financeRoleCode", financeRoleCode);
        return returnMap;
    }

    /**
     * 如果当前待绑定的角色为财务，则注册供应链金融
     * @param map
     * @return
     */
    public boolean registryToBanking(Map map){
        String financeRoleCode = parseConfRoleInfo().get("financeRoleCode").toString();
        List<MdRole> mdRoleList = mdRoleDao.selectAllByConditon(map);
        boolean isFinance = false;
        for(MdRole mdRole : mdRoleList){
            if(StringUtils.isNotEmpty(mdRole.getRoleCode()) && mdRole.getRoleCode().equalsIgnoreCase(financeRoleCode)){
                isFinance = true;
            }
        }
        if(!isFinance){
            logger.info("MdAccountServiceImpl => 当前待设置的角色中不包含财务角色，不进行下一步操作!");
            return false;
        }

        // { "CUSTOMERCODE": "chendai001", "CELLPHONE": "18344016302" }
        String CUSTOMERCODE = map.get("account").toString();
        String CELLPHONE = map.get("vendorCode").toString();
        JSONObject httpParams = new JSONObject();
        httpParams.put("CUSTOMERCODE", CUSTOMERCODE);
        httpParams.put("CELLPHONE", CELLPHONE);

        String url = PropertyConfigurer.parseStr("bank.url.registry");
        String username = PropertyConfigurer.parseStr("bank.auth.username");
        String password = PropertyConfigurer.parseStr("bank.auth.password");
        // { "STATUS" : "201", "MESSAGE" : "失败"}
        String httpResponse = HttpClientHelper.invokeAuth(url,httpParams.toString(),username,password);
        JSONObject jsonObject = JSON.parseObject(httpResponse);
        Object status = jsonObject.get("STATUS");
        if(null == status || !status.toString().equals("200")){
            // true 代表着整个操作是失败的
            return true;
        }
        return false;
    }

    /**
     * 判断当前角色列表中是否存在包含负责人角色 ：暂定为 ：R00001
     *
     * @param mdRoleList
     * @return
     */
    public boolean isContainMainRole(List<MdRole> mdRoleList) {
        if (null == mdRoleList || mdRoleList.size() == 0) {
            return false;
        }
        String mainRoleCode = PropertyConfigurer.parseStr("mdrole.main.roleCode");
        for (MdRole mdRole : mdRoleList) {
            if (mdRole.getRoleCode().toUpperCase().equals(mainRoleCode.toLowerCase())) {
                return true;
            }
        }
        return false;
    }


    /**
     * 构建账户与角色关系实体列表参数
     *
     * @param map
     * @param accountId
     * @return
     */
    public List<MdAccountRole> buildMdAccountRole(Map map, int accountId) {
        Object roleIds = map.get("roleIds") != null ? map.get("roleIds") : null;
        List<MdAccountRole> mdAccountRoleList = new ArrayList<MdAccountRole>();
        String[] roleIdArr = null;
        if (roleIds instanceof String) {
            roleIdArr = roleIds.toString().split(",");
        }
        if (roleIds instanceof String[]) {
            roleIdArr = (String[]) roleIds;
        }
        for (String str : roleIdArr) {
            MdAccountRole mdAccountRole = new MdAccountRole();
            mdAccountRole.setAccountId((long) accountId);
            mdAccountRole.setRoleId(Integer.parseInt(str));
            mdAccountRoleList.add(mdAccountRole);
        }
        return mdAccountRoleList;
    }

    /**
     * 构建子账户实体
     *
     * @param map 请求参数
     * @return 返回子账户实体
     */
    public MdAccount buildMdAccount(Map map) {
        MdAccount mdAccount = new MdAccount();
        mdAccount.setAccount(map.get("account").toString());
        mdAccount.setName(map.get("name").toString());
        mdAccount.setPasswdType(map.get("passwdType") != null ? map.get("passwdType").toString() : "D");
        mdAccount.setAccountType(map.get("accountType") != null ? map.get("accountType").toString() : "A");
        return mdAccount;
    }

    public MdAccount selectByAccount(String account) {
        return mdAccountDao.selectByAccount(account);
    }

    public List<MdAccount> selectByCondition(Integer vendorId) {
        return mdAccountDao.selectByCondition(vendorId);
    }

    public int updateOpenId(MdAccount account) {
        return mdAccountDao.updateOpenId(account);
    }

    @Transactional
    public int addAccount(String account, Integer vendorId) throws Exception {

        // 添加供应商子账号
        MdAccount mdAccount = new MdAccount();
        mdAccount.setAccount(account);
        mdAccount.setAccountType(ConstantDefine.ACCOUNT_TPYE_CHILD);
        mdAccount.setPasswdType("D");
        mdAccount.setIsBind(ConstantDefine.ACCOUNT_BLIANDWX_NO);
        int resultCode = mdAccountDao.insert(mdAccount);
        if (resultCode < 0) {
            throw new Exception("account insert into db failed with " + mdAccount);
        }

        // 添加供应商与子账号关系
        MdVendorAccount mdVendorAccount = new MdVendorAccount();
        mdVendorAccount.setAccountId(mdAccount.getAccountId());
        mdVendorAccount.setVendorId(vendorId);
        mdVendorAccount.setType(ConstantDefine.ACCOUNT_TPYE_CHILD);
        resultCode = mdVendorAccountDao.insert(mdVendorAccount);
        if (resultCode < 0) {
            throw new Exception("mdVendorAccount insert into db failed with " + mdVendorAccount);
        }

        return resultCode;
    }

    public int updateStatus(Map map){
		return mdAccountDao.updateStatus(map);
	}
	
}
